import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { plainToClass } from 'class-transformer';
import { EXCEPTION } from 'src/common/const/constants';
import { PageResDto } from 'src/common/dto/page.dto';
import { CommonException } from 'src/common/exception/common.exception';
import { MinioClientService } from 'src/core/system/minio/minio.service';
import { User } from 'src/core/system/user/entities/user.entity';
import { DataSource, Repository } from 'typeorm';
import { Pic } from '../album/dto/create-album.dto';
import { SongDto } from '../song/dto/search-song.dto';
import { Song } from '../song/entities/song.entity';
import { SongService } from '../song/song.service';
import { DTrack } from './dao/dtrack.dao';
import { FavoriteForm, TrackForm } from './dto/create-track.dto';
import { ReCommendTrackInfoDto, TrackInfoDto, TrackPageQuery } from './dto/search-tracklist.dto';
import { Track } from './entities/track.entity';

@Injectable()
export class TrackService {
  constructor(
    @InjectRepository(Track) private trackRepository: Repository<Track>,
    private dataSource: DataSource,
    private minioClientService: MinioClientService,
    private songService: SongService,
    private dtrack: DTrack
  ) { }

  /**
  * 专辑-删除-我收藏的专辑
  * @param songForm 
  * @returns 
  */
  deletefavorite(user: User, songForm: FavoriteForm) {
    this.dtrack.deleteFavorite(user.id, songForm)
  }
  /**
   * 专辑-新增-我收藏的专辑
   * @param songForm 
   * @returns 
   */
  createfavorite(user: User, songForm: FavoriteForm) {
    this.dtrack.createFavorite(user.id, songForm)
  }

  /**
  * 专辑-我收藏的专辑
  * @param pageDto 
  * @returns 
  */
  async myfavorite(user: User, page: TrackPageQuery) {
    const [data, total] = await this.dtrack.myfavorite(user?.id, page);
    let songVos: TrackInfoDto[] = await this.getList(user, data);
    return PageResDto.result(songVos, total)
  }
  /**
   * 歌单-保存
   * @param trackForm 
   */
  async saveTrack(trackForm: TrackForm) {
    const { trackIds, pic } = trackForm
    const track = plainToClass(Track, trackForm)


    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {

      const res = await queryRunner.manager
        .createQueryBuilder()
        .insert()
        .into(Track)
        .values(track)
        .execute();
      const { id } = res.identifiers.at(0);

      await queryRunner.manager
        .createQueryBuilder()
        .relation(Track, 'pic')
        .of(id)
        .set(pic.id);

      if (trackIds.length > 0) {
        await queryRunner.manager
          .createQueryBuilder()
          .relation(Track, 'songList')
          .of(id)
          .add(trackIds);

        let songList = await queryRunner.manager
          .createQueryBuilder(Song, 'song')
          .leftJoinAndSelect('song.genre', "genre")
          .select([
            'song.id',
            'song.title',
            'genre.id',
            'genre.categoryName'
          ])
          .where("song.id IN (:...ids)", { ids: trackIds })
          .getMany()

        let cateList = Array.from(new Set(songList.flatMap(song => song.genre.map(g => g.id))))

        trackForm.tagIds = cateList
      }


      if (trackForm.tagIds.length > 0) {
        await queryRunner.manager
          .createQueryBuilder()
          .relation(Track, 'tagList')
          .of(id)
          .add(trackForm.tagIds);
      }

      await queryRunner.commitTransaction();
    } catch (error) {
      await queryRunner.rollbackTransaction();
      throw new CommonException(EXCEPTION.COMMON);
    } finally {
      await queryRunner.release();
    }

  }

  /**
   * 歌单-修改
   * @param id 
   * @param updateTrackDto 
   * @returns 
   */
  async update({ username }: User, id: number, trackForm: TrackForm) {
    const { pic } = trackForm
    const oldTrack = await this.dtrack.getOne(id, username)
    const track = plainToClass(Track, trackForm)
    const songs = trackForm.trackIds.map(id => new Song({ id }))
    //封面
    if (!oldTrack.pic) {
      const pictureFile = await this.minioClientService.findOne(pic.id)
      track.pic = pictureFile
    } else if (oldTrack.pic && oldTrack.pic.id !== pic.id) {
      await this.minioClientService.deleteFileById(oldTrack.pic.id)
      const pictureFile = await this.minioClientService.findOne(pic.id)
      track.pic = pictureFile
    }

    track.songList = songs

    this.trackRepository.save(track)

  }

  /**
   * 歌单-删除
   * @param ids 
   */
  removeByIds(ids: number[]) {
    this.trackRepository.delete(ids)
  }

  /**
   * 歌单-获取单个数据
   * @param req 
   * @param id 
   */
  async getTrackFormData(user: User, id: number) {

    const data = await this.dtrack.getOne(id, user?.username);

    let trackVo: TrackInfoDto = plainToClass(TrackInfoDto, data)
    if (data) {
      trackVo.pic = plainToClass(Pic, data.pic)
    }

    let songVos: SongDto[] = []

    for await (const song of data.songList) {

      let songVo: SongDto = plainToClass(SongDto, song);

      if (user?.username) {
        songVo.isFavorite = song.favoriteUsers.length > 0;
      }

      songVos.push(songVo)
    }

    trackVo.songList = songVos


    return trackVo

  }


  /**
   * 歌单-分页查询
   * @param param0 
   * @param param1 
   * @returns 
   */
  async page(user: User, { pageNum, pageSize, trackName, tagId }: TrackPageQuery) {

    const [data, total] = await this.dtrack.page(user?.username, pageNum, pageSize, trackName, tagId);

    const trackVos = await this.getList(user, data)

    return PageResDto.result(trackVos, total)
  }

  /**
   * 歌单-推荐列表-查询
   * @param param0 
   * @param param1 
   * @returns 
   */
  async getRecommendList(user: User, { pageNum, pageSize }: TrackPageQuery) {

    const [data, total] = await this.dtrack.getRecommendList(user?.username, pageNum, pageSize);

    let trackVos: ReCommendTrackInfoDto[] = []

    for await (const track of data) {

      let trackVo: ReCommendTrackInfoDto = plainToClass(ReCommendTrackInfoDto, track);

      if (user?.username) {
        trackVo.isFavorite = track.favoriteUsers.length > 0;
      }

      trackVo.recommendSort = track.id
      trackVo.pic = plainToClass(Pic, track.pic)

      trackVos.push(trackVo)
    }

    return PageResDto.result(trackVos, total)
  }

  async getList(user: User, data: Track[]) {
    let trackVos: TrackInfoDto[] = []

    for await (const track of data) {

      let trackVo: TrackInfoDto = plainToClass(TrackInfoDto, track);

      if (user?.username) {
        trackVo.isFavorite = track.favoriteUsers.length > 0;
      }

      trackVo.pic = plainToClass(Pic, track.pic)

      trackVos.push(trackVo)
    }
    return trackVos
  }

}


